#include "RenderSystem.h"
#include <GL/glext.h>
#include "DDS Loader/dds_loader.h"
#include <cmath>
#include <string>
#include "Log.h"

PFNGLPOINTPARAMETERFARBPROC  glPointParameterfARB  ;
PFNGLPOINTPARAMETERFVARBPROC glPointParameterfvARB ;

class RenderModule		
{
public:
	RenderModule()
	{
		glPointParameterfARB  = (PFNGLPOINTPARAMETERFEXTPROC)wglGetProcAddress("glPointParameterfARB");
		glPointParameterfvARB = (PFNGLPOINTPARAMETERFVEXTPROC)wglGetProcAddress("glPointParameterfvARB");

		PFNGLCOMPRESSEDTEXIMAGE2DARBPROC pFn = 
			(PFNGLCOMPRESSEDTEXIMAGE2DARBPROC)wglGetProcAddress("glCompressedTexImage2D");
		DDS_Init(pFn);
	}
	~RenderModule()
	{
		//DDS_DeInit();
	}

};
//class RenderObject
//{
//public:
	//Point3D	Pos;	//Position
	//Point3D Dir;	//Direction
	//Point3D Color;	//X = r, Y = g, Z = b. Interval: [0..1]
	//Point3D	Size;	//X - Width; Y - Height; Z - Depth

//Servicing definition's pack
RenderObject::RenderObject()
{
	Size.X=1;Size.Y=1;Size.Z=1;
	Pos.X=0;Pos.Y=0;Pos.Z=0;
	Rot.X=0;Rot.Y=0;Rot.Z=0;
	Color.X = 1; Color.Y = 1; Color.Z = 1;
	glEnable(GL_CULL_FACE);
}

RenderObject::RenderObject(const RenderObject& copy)
{
	Size = copy.Size;
	//Size.X = copy.Size.X;	Size.Y = copy.Size.Y;	Size.Z = copy.Size.Z;
	Rot = copy.Rot;
	Pos = copy.Pos;
	Color = copy.Color;
	m_uiTexture = copy.m_uiTexture;
	m_Normals	= copy.m_Normals;
	m_TexCoords = copy.m_TexCoords;
	m_Faces		= copy.m_Faces;
	m_Flags		= copy.m_Flags;
}
void RenderObject::Load(const char *szFileName)
{
	m_Flags=0;
	static const RenderModule rend;
	std::string	sName3DS=szFileName;
	sName3DS+=".3ds";
	if(m_Load3DS(sName3DS.c_str()))
	{
		m_Flags |= IsLoaded;
		
	}else
	{
		LOG_ERROR("Cant load model!");
		Unload();
	}

	
	std::string	sNameDDS=szFileName;
	sNameDDS+=".dds";
	if(m_Flags & IsUseTexture)
	{
		DDS_IMAGE_INFORMATION	dds_info;
		memset(&dds_info, 0, sizeof(DDS_IMAGE_INFORMATION));
		if(0!=DDS_LoadFile(sNameDDS.c_str(), m_uiTexture, &dds_info))
			m_Flags &= IsUseTexture;
	}
}
void RenderObject::Render()
{
	if(!(m_Flags & IsLoaded) || !Renderable)return;

	glPushAttrib(GL_MATRIX_MODE);
	glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(45, 800./600., 0.01, 90.0);


	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	
	//gluLookAt(0, 0, 1, 0, 0, 0, 0, 1, 0);
	
	//glEnable(GL_RESCALE_NORMAL);

	glTranslated(Pos.X, Pos.Y, Pos.Z);
	glScalef(Size.X, Size.Y, Size.Z);
	glRotatef(Rot.X*180.0f/3.141593f, 1, 0, 0);
	glRotatef(Rot.Y*180.0f/3.141593f, 0, 1, 0);
	glRotatef(Rot.Z*180.0f/3.141593f, 0, 0, 1);

#ifdef _DEBUG

	glDisable(GL_LIGHTING);
	glBegin(GL_LINES);
	glColor3f(1.0f, 0.0f, 0.0f);
	glVertex3f(0.0f, 0.0f, 0.0f);
	glVertex3f(1.0f, 0.0f, 0.0f);

	glColor3f(0.0f, 1.0f, 0.0f);
	glVertex3f(0.0f, 0.0f, 0.0f);
	glVertex3f(0.0f, 1.0f, 0.0f);

	glColor3f(0.0f, 0.0f, 1.0f);
	glVertex3f(0.0f, 0.0f, 0.0f);
	glVertex3f(0.0f, 0.0f, 1.0f);
	glEnd();
#endif
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	static float loli=0;
	//loli+=0.0001f;
	GLfloat pos[]={0.0f, 0.0f, 3.0f, 1.0f};
	glLightfv(GL_LIGHT0, GL_POSITION, pos);
		
	//glShadeModel(GL_FLAT);
	
	/*GLUquadricObj	*torus;
	torus = gluNewQuadric();
	gluQuadricOrientation(torus, GLU_OUTSIDE);
	gluQuadricNormals(torus, GLU_FLAT);
	gluQuadricDrawStyle(torus, GLU_FILL);
	gluSphere(torus, 1.0f, 16, 16);
	gluCylinder(torus, 0.25f, 0.25f, 0.5f, 8, 8);
	gluDisk(torus, 0.25f, 0.5f, 10, 10);
	gluDeleteQuadric(torus);*/
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);
	
	glColor4f(Color.X, Color.Y, Color.Z, 1.0f);
	glNormalPointer(GL_FLOAT, 0, &(m_Normals[0]));
	glVertexPointer(3, GL_FLOAT, 0, &(m_Vertexes[0]));

	glEnable(GL_CULL_FACE);
	if(m_Flags & IsUseTexture)
	{
		glEnable(GL_TEXTURE_2D);
		glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glBindTexture(GL_TEXTURE_2D, m_uiTexture);
		glTexCoordPointer(2, GL_FLOAT, 0, &(m_TexCoords[0]));
	};//else glBindTexture(GL_TEXTURE_2D, 0);
	glDepthFunc(GL_TRUE);
	glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_Vertexes.size());
	//glDrawElements(GL_TRIANGLES, m_Faces.size()*3, GL_UNSIGNED_SHORT, &m_Faces[0]);
	glEnable(GL_LIGHTING);
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);
	if(m_Flags & IsUseTexture)
	{
		glDisable(GL_TEXTURE_2D);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}
	glPopMatrix();
	glPopAttrib();
}

void RenderObject::Unload()
{
	if(m_Flags & IsUseTexture)
		m_fnUnloadTexture(m_uiTexture);
	
	m_Vertexes.clear();
	m_Normals.clear();
	m_TexCoords.clear();
	m_Flags=0;	
};

//This function is auxiliary for m_Load3DS (see bellow)
unsigned int	RenderObject::m_Find3DSChunk(std::ifstream &ModelsFile, unsigned short int usChunk, bool fParent)
{
	/*This function is getting a link to the file's stream, ID of block, and bool var,
	which say to us, how function must to search the block: from parent's block, or inside
	him. It is needing for searching sub blocks of one's type at one parent's block
	(at perspective)
	
	Attention: this code has get from gamedev.ru (http://www.gamedev.ru/code/articles/?id=4274&page=4)
											and it VERY slow
	*/
	const unsigned short int EDIT_OBJECT=0x4000;

    unsigned short int	usChunkId;
    unsigned int		uiChunkLength;
    
    if(fParent)
    {
        ModelsFile.read((char*)&usChunkId,2);
        ModelsFile.read((char*)&uiChunkLength,4);
        //If is it object's block, we are passing object's name (string with 0)
        if(usChunkId==EDIT_OBJECT)
        {
            char ch;
            do
            {
                ModelsFile.read((char*)&ch,1);
            }while(ch!='\0' && !ModelsFile.eof());
        }
    }
    //Beginning the searching in parent's block
    do
    {
        //Reading block's ID, and his length
        ModelsFile.read((char*)&usChunkId,2);
        ModelsFile.read((char*)&uiChunkLength,4);
        //Passing all, except object's block
        if(usChunkId!=usChunk) ModelsFile.ignore(uiChunkLength-6u);
        else
        {
            //Let's return to the begin of founded block and return his position
            ModelsFile.seekg((unsigned int)ModelsFile.tellg()-6u);
            return (ModelsFile.tellg());
        }
    }while(!ModelsFile.eof());
    return false;
}
//---------------------------------------------------------------------------
bool RenderObject::m_Load3DS(const char *szFileName)
{
    const unsigned short int MAIN3DS			=0x4D4D;		//ID of *.3ds - file
    const unsigned short int EDIT3DS			=0x3D3D;		//Editor's block (this is the start of the editor config)
    const unsigned short int EDIT_OBJECT		=0x4000;	//Object (Mesh, Light sources, Camera)

    //------ sub defines of EDIT_OBJECT
    const unsigned short int OBJ_TRIMESH		=0x4100;

    //------ sub defines of OBJ_TRIMESH
    const unsigned short int TRI_VERTEXLIST	=0x4110;
    const unsigned short int TRI_VERTEXOPTIONS	=0x4111;
    const unsigned short int TRI_MAPPINGCOORS	=0x4140;
    const unsigned short int TRI_MAPPINGSTANDARD=0x4170;
    const unsigned short int TRI_FACELIST		=0x4120;
    const unsigned short int TRI_SMOOTH		=0x4150;
    const unsigned short int TRI_MATERIAL		=0x4130;
    const unsigned short int TRI_LOCAL			=0x4160;
    const unsigned short int TRI_VISIBLE		=0x4165;
    //Another blocks don't worried us :)
    unsigned short int	usChunkID;	//Block's ID
    unsigned int		uiChunkPos;	//Position of block's begin
    unsigned int		uiChunkTempPos;//Temp -**-
    unsigned int		uiChunkLength;//Offset to next block (from chunk_id)

    //Opening 3DS-file
	std::ifstream File3DS;
	File3DS.open(szFileName, std::ios::binary | std::ios::beg);
    if(!File3DS.is_open())
    {
        //Can't open file
#ifdef _DEBUG
		std::string sError = "Can't load 3ds-file: ";
		sError.append(szFileName);
		LOG_ERROR(sError);
#endif
        return false;
    }
    else
    {
        //Testing: it is 3ds-file?
        File3DS.read((char*)&usChunkID, 2);
        File3DS.read((char*)&uiChunkLength, 4);
        if(usChunkID!=MAIN3DS) return false;
        File3DS.seekg((unsigned int)File3DS.tellg()-6u);
        
		//Next block must be editor's block
        uiChunkPos=m_Find3DSChunk(File3DS, EDIT3DS);
        if(uiChunkPos==0) return false;

        //Passing all, except object's block
        uiChunkPos=m_Find3DSChunk(File3DS, EDIT_OBJECT);
        if(uiChunkPos==0) return false;

        //Reading object's name (If we need it )
        //...
        //Passing all, except object's block
        uiChunkPos=m_Find3DSChunk(File3DS,OBJ_TRIMESH);
        if(uiChunkPos==0) return false;

        //Keep in mind OBJ_TRIMESH
        uiChunkTempPos=uiChunkPos;

        //Go to the vertex's block
        uiChunkPos=m_Find3DSChunk(File3DS,TRI_VERTEXLIST);
        if(uiChunkPos==0) return false;
        File3DS.ignore(6);

        //Reading vertexes
		unsigned short int nVertexes;
        File3DS.read((char*)&nVertexes,2);
		Point3D *pVertexes=new Point3D[nVertexes];
		File3DS.read((char*)pVertexes, nVertexes*sizeof(Point3D));
        

		//Go to face's list
		//Reading faces
		unsigned short int nFaces=0;
		unsigned short int *pFaces=0;
		File3DS.seekg(uiChunkTempPos);
		uiChunkPos=m_Find3DSChunk(File3DS,TRI_FACELIST);
		if(uiChunkPos!=0)
		{
			if(uiChunkPos==0) return false;
			File3DS.ignore(6);


			File3DS.read((char*)&nFaces,2);
			pFaces=new unsigned short int[nFaces*4];	//three vertexes and one flag
			File3DS.read((char*)pFaces, nFaces*4*sizeof(unsigned short int));
		}
		else
		{
			m_Faces.resize(m_Vertexes.size()/3+1);
			for(unsigned short ui=0; ui<m_Vertexes.size(); ui+=3)
			{
				m_Faces[ui/3].v1 = ui;
				m_Faces[ui/3].v2 = ui+1;
				m_Faces[ui/3].v3 = ui+2;
			}
		}

        //Go to texture's coordinates
		Point2D				*pTexCoords=0;
		unsigned short int nTexCoords;

        File3DS.seekg(uiChunkTempPos);
        uiChunkPos=m_Find3DSChunk(File3DS,TRI_MAPPINGCOORS);
        if(uiChunkPos==0) 
		{
			File3DS.seekg(uiChunkTempPos);
			uiChunkPos = uiChunkTempPos;
			m_Flags &= IsUseTexture;
		}else
		{
			m_Flags |= IsUseTexture;
			File3DS.ignore(6);

			//Reading texture's coordinates
			File3DS.read((char*)&nTexCoords,2);

			pTexCoords = new Point2D[nVertexes];
			File3DS.read((char*) pTexCoords, sizeof(Point2D)*nTexCoords);
		}
        File3DS.close();

		//Splicing "faces" to our faces and flags
		std::vector<unsigned short int> Flags;
		Flags.resize(nFaces);
		if(nFaces!=0)
		{
			m_Faces.resize(nFaces);
			
			unsigned short int usi=0;
			for(unsigned short ui = 0; ui < m_Faces.size(); ui++)
			{
				usi = ui *4;
				m_Faces[ui].v1 = pFaces[usi];
				m_Faces[ui].v2 = pFaces[usi+1];
				m_Faces[ui].v3 = pFaces[usi+2];
				Flags[ui]	   = pFaces[usi+3];
			}

			delete[] pFaces;
		}

		for(unsigned int ui=0; ui <m_Vertexes.size(); ui++)
			m_fnPoint3DChangeYZ(m_Vertexes[ui]);

		//Calculating vertexes from faces and old vertexes
		m_Vertexes.resize(m_Faces.size()*3);
		for(unsigned int ui=0; ui<m_Faces.size(); ui++)
		{
			m_Vertexes[ui*3] = pVertexes[m_Faces[ui].v1];
			m_Vertexes[ui*3+1] = pVertexes[m_Faces[ui].v2];
			m_Vertexes[ui*3+2] = pVertexes[m_Faces[ui].v3];
		}
		

		if(m_Flags & IsUseTexture)
		{
			m_TexCoords.resize(m_Faces.size()*3);
			for(unsigned int ui=0; ui<m_Faces.size(); ui++)
			{
				m_TexCoords[ui*3]	= pTexCoords[m_Faces[ui].v1];
				m_TexCoords[ui*3+1] = pTexCoords[m_Faces[ui].v2];
				m_TexCoords[ui*3+2] = pTexCoords[m_Faces[ui].v3];
			}
		}
		delete[] pTexCoords;

		//Calculating normals
		m_Normals.resize(m_Vertexes.size());
		for(unsigned int ui=0; ui<m_Vertexes.size(); ui+=3)
		{
			Point3D	Vector1(
				m_Vertexes[ui+1].X- m_Vertexes[ui].X,
				m_Vertexes[ui+1].Y- m_Vertexes[ui].Y,
				m_Vertexes[ui+1].Z- m_Vertexes[ui].Z);

			Point3D	Vector2(
				m_Vertexes[ui+2].X- m_Vertexes[ui].X,
				m_Vertexes[ui+2].Y- m_Vertexes[ui].Y,
				m_Vertexes[ui+2].Z- m_Vertexes[ui].Z);
				

			m_Normals[ui].X = -1.0f*(Vector1.Y*Vector2.Z-Vector2.Y*Vector1.Z);
			m_Normals[ui].Y = -1.0f*(Vector1.X*Vector2.Z-Vector2.X*Vector1.Z);
			m_Normals[ui].Z = -1.0f*(Vector1.X*Vector2.Y-Vector2.X*Vector1.Y);

			//if(!(Flags[ui] &0x0006))
			{
				//m_Normals[m_Faces[ui].v1].X *= -1;
				//m_Normals[m_Faces[ui].v1].Y *= -1;
				//m_Normals[m_Faces[ui].v1].Z *= -1;
			}

			m_Normals[ui+2].X = m_Normals[ui+1].X = m_Normals[ui].X;
			m_Normals[ui+2].Y = m_Normals[ui+1].Y = m_Normals[ui].Y;
			m_Normals[ui+2].Z = m_Normals[ui+1].Z = m_Normals[ui].Z;
			//if(Flags[ui]==
		}
		
        return true;
    }
}

Camera::Camera()
{
	m_alpha=270.0f;	
	m_beta=180;
	m_distance=2;	//Distance from focus object to camera

	Point3D	m_Focus=Point3D(0,0,0);
}

void Camera::SetAlpha(GLfloat Alpha)
{
	//while(Alpha>=360)Alpha-=360;
	//while(Alpha<0)Alpha+=360;

	//if(Alpha<0)Alpha+=360;
	//if(Alpha>180)
	//{
	//	Alpha = (360-Alpha);
	//	//Alpha -= 180 - 2*(180-2*Alpha);
	//	SetBeta(GetBeta()-180.0f);
	//}
	m_alpha=Alpha/180.0f*3.141592f;
}
void Camera::SetBeta(GLfloat Beta)
{
	
	//while(Beta>=360)Beta-=360;
	//while(Beta<0)Beta+=360;
	m_beta=Beta/180.0f*3.141592f;
}
void Camera::SetDistance(GLfloat Distance)
{
	if(Distance>0)m_distance = Distance;
}

Point3D Camera::GetPosition()const
{
	Point3D	EyePoint=m_Focus;
	GLfloat alpha=PreA(), beta=PreB();;
	EyePoint.X += sinf(alpha)*cosf(beta)*m_distance;
	EyePoint.Y += sinf(alpha)*sinf(beta)*m_distance;
	EyePoint.Z += cosf(alpha)*m_distance;
	return EyePoint;
}
void Camera::Activate()const
{
	const GLfloat	delta=0.001f;
	GLfloat alpha= PreA(), beta=PreB();;
	Point3D	EyePoint=GetPosition();	

	Point3D	UpVector = m_Focus;
	UpVector.X += sinf(alpha+delta)*cosf(beta)*m_distance;
	UpVector.Y += sinf(alpha+delta)*sinf(beta)*m_distance;
	UpVector.Z += cosf(alpha+delta)*m_distance;
	UpVector.X -= EyePoint.X;
	UpVector.Y -= EyePoint.Y;
	UpVector.Z -= EyePoint.Z;



	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(EyePoint.X, EyePoint.Y, EyePoint.Z, m_Focus.X, m_Focus.Y, m_Focus.Z, UpVector.X, UpVector.Y, UpVector.Z);
	
}

SpecialFX::SpecialFX(const SpecialFX& copy):RenderObject(copy)
{
	m_Intensity		= copy.m_Intensity;		//Particles at millisecond
	m_uiLifeTime	= copy.m_uiLifeTime;
	m_nParticles	= copy.m_nParticles;

	m_Particles		= copy.m_Particles;
	m_States		= copy.m_States;
	m_Colors		= copy.m_Colors;

	m_PointSize		= copy.m_PointSize;
	m_fPaused		= copy.m_fPaused; //Update willn't add new particles, but will update old particles
}
void SpecialFX::Load(const char *szFileName)
{
	DDS_IMAGE_INFORMATION imgInfo;
	DDS_LoadFile(szFileName, m_uiTexture, &imgInfo);
	glPointParameterfARB(GL_POINT_SIZE_MAX_ARB, 1.0f);
	m_uiLifeTime = 2000; 
	m_Intensity = 0.5f;
}

void SpecialFX::Render()
{
	if(m_Vertexes.empty())return;
	glPushAttrib(GL_MATRIX_MODE);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45, 800./600., 0.01, 90.0);
	glDepthMask(GL_FALSE);

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glTranslated(Pos.X, Pos.Y, Pos.Z);
	glScalef(Size.X, Size.Y, Size.Z);
	glRotatef(Rot.X, 1, 0, 0);
	glRotatef(Rot.Y, 0, 1, 0);
	glRotatef(Rot.Z, 0, 0, 1);

	float quadratic[] =  { 0.1f, 0.1f, 0.1f, 0.00f };
	
	glEnable( GL_POINT_SPRITE_ARB );
	glPointParameterfvARB( GL_POINT_DISTANCE_ATTENUATION_ARB, quadratic );
	glPointSize( m_PointSize );
	glPointParameterfARB( GL_POINT_SIZE_MAX_ARB, m_PointSize+1.0f );
	glPointParameterfARB( GL_POINT_SIZE_MIN_ARB, 0.1f );

	glDisable(GL_LIGHTING);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, m_uiTexture);
	glTexEnvf( GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE );
	
	//glEnableClientState(GL_POINT_SIZE_ARRAY_OES);
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_COLOR_ARRAY);
	
	//glPointSizePointerOES();
	glVertexPointer(3, GL_FLOAT, 0, &(m_Vertexes[0]));
	glColorPointer(4, GL_FLOAT, 0, &(m_Colors[0]));
	glPointSize( m_PointSize );
	
	glDrawArrays(GL_POINTS, 0, m_Particles.size()-1);
	
	glDisableClientState(GL_COLOR_ARRAY);
	glDisableClientState(GL_VERTEX_ARRAY);
	//glDisableClientState(GL_POINT_SIZE_ARRAY_OES);
	
	glPopMatrix();
	glPopAttrib();
	glDisable(GL_TEXTURE_2D);
	glDisable( GL_POINT_SPRITE_ARB );
	glDepthMask(GL_TRUE);
}

void SpecialFX::Update(unsigned int uiTime)
{
	static Particle	newParticle;
	unsigned int	uiIndex=0;
	//std::list<Particle>::iterator tempIter;
	for(std::vector<Particle>::iterator iter = m_Particles.begin(); iter!=m_Particles.end(); iter++, uiIndex++)
	{
		iter->uiLifeTime += uiTime;
		if(iter->uiLifeTime > m_uiLifeTime)
		{
			if(!m_fPaused)
			{
				newParticle = m_fnGenParticle();
				m_Vertexes[uiIndex].X = newParticle.x; 
				m_Vertexes[uiIndex].Y = newParticle.y; 
				m_Vertexes[uiIndex].Z = newParticle.z;
				m_Colors[uiIndex].R = newParticle.r;
				m_Colors[uiIndex].G = newParticle.g;
				m_Colors[uiIndex].B = newParticle.b;
				m_Colors[uiIndex].A = newParticle.a;
			}else
				m_Colors[uiIndex].A = 0;
			
			iter->vx = newParticle.vx; iter->vy = newParticle.vy; iter->vz = newParticle.vz;
			iter->uiLifeTime=0;
			continue;
		}

		if(iter->uiLifeTime<m_uiLifeTime)
			m_fnUpdateParticle(uiTime, uiIndex);


		//iter->vy += 0.0002f*((float)uiTime)/1000.0f;
	}	
	//Particle	newParticle;

	if(!m_fPaused)
	if(m_Particles.size()<m_nParticles)
	for(unsigned int ui = 0; ui < (float)m_Intensity*(float)uiTime; ui++)
	{
		newParticle = m_fnGenParticle();
		m_Vertexes.push_back(Point3D(newParticle.x, newParticle.y, newParticle.z));
		m_Colors.push_back(Colour(newParticle.r, newParticle.g, newParticle.b, newParticle.a));
		m_Particles.push_back(newParticle);
		if(ui>m_nParticles)break;
	}
}

Particle SpecialFX:: m_fnGenParticle()
{
	Particle result;
	static float fRand=0;
	fRand = rand()%100-50.0f;
	result.a = 0.5f + (float)fRand/30000.0f;
	result.r = 1.0f + (float)fRand/3000.0f;
	result.g = 0.8f + (float)fRand/3000.0f;
	result.b = 0.5f + (float)fRand/3000.0f;
	result.x = 0.0f;
	result.y = 0.0f;
	result.z = 0.0f;
	result.uiLifeTime = 0;
	result.vx = (float)fRand/30000.0f;
	result.vy = (float)-1.0f*fRand/15000.0f;
	result.vz = (float)fRand/30000.0f;
	return result;
}

void SpecialFX::m_fnUpdateParticle(unsigned int uiTime, unsigned int uiParticle)
{
	m_Colors[uiParticle].R+=(float)uiTime/(float)m_uiLifeTime;
	m_Colors[uiParticle].G-=(float)uiTime/(float)m_uiLifeTime;
	m_Colors[uiParticle].B-=(float)uiTime/(float)m_uiLifeTime;
	m_Colors[uiParticle].A-=(float)uiTime/(float)m_uiLifeTime;

	m_Vertexes[uiParticle].X+=m_Particles[uiParticle].vx*(float)uiTime;
	m_Vertexes[uiParticle].Y+=m_Particles[uiParticle].vy*(float)uiTime;
	m_Vertexes[uiParticle].Z+=m_Particles[uiParticle].vz*(float)uiTime;
}

Explosion::Explosion():SpecialFX()
{ 
		m_nParticles = 1000; 
		m_Intensity = 300;
		m_uiLastTime=0;
		m_uiLifeTime = 50000;
};
Explosion::Explosion(const Explosion &copy):SpecialFX(copy)
{
	m_uiLastTime			= copy.m_uiLastTime;
	m_nParticles			= copy.m_nParticles; 
	m_Intensity				= copy.m_Intensity;
	m_uiLastTime			= copy.m_uiLastTime;
	m_uiLifeTime			= copy.m_uiLifeTime;
}
Explosion::~Explosion()
{
	;
}

void Explosion::Reset()
{
	m_uiLastTime=0; 
	m_fPaused=false;
};

bool Explosion::IsCompleted()
{
	return m_uiLastTime>m_uiActiveTime?true:false;
};
	
void Explosion::Update(unsigned int uiTime)
{

	SpecialFX::Update(uiTime/1000);
	if(m_uiLastTime>m_uiActiveTime)
		m_fPaused=true; 
	m_uiLastTime+=uiTime; 
};

Particle Explosion:: m_fnGenParticle()
{
	Particle result;
	result.a = 0.5f + (float)(rand()%100-50.0f)/30000.0f;
	result.r = 1.0f + (float)(rand()%100-50.0f)/3000.0f;
	result.g = 0.8f + (float)(rand()%100-50.0f)/3000.0f;
	result.b = 0.5f + (float)(rand()%100-50.0f)/3000.0f;
	result.x = (float)(rand()%100-50)/100.0f;
	result.y = (float)(rand()%100-50)/100.0f;
	result.z = 0.0f;
	result.uiLifeTime = 0;
	result.vx = (float)(rand()%100-50.0f)/15000.0f;
	result.vy = (float)(rand()%100-50.0f)/15000.0f;
	result.vz = (float) 1.0f*(rand()%100)/15000.0f;
	return result;
}

void Explosion::m_fnUpdateParticle(unsigned int uiTime, unsigned int uiParticle)
{
	m_Colors[uiParticle].R+=1.5f*(float)uiTime/(float)m_uiLifeTime;
	m_Colors[uiParticle].G-=1.5f*(float)uiTime/(float)m_uiLifeTime;
	m_Colors[uiParticle].B-=1.5f*(float)uiTime/(float)m_uiLifeTime;
	m_Colors[uiParticle].A-=(float)uiTime/(float)m_uiLifeTime;

	m_Particles[uiParticle].vz -= 9.8f*(float)uiTime/875000.0f;
	m_Vertexes[uiParticle].X+=m_Particles[uiParticle].vx*(float)uiTime;
	m_Vertexes[uiParticle].Y+=m_Particles[uiParticle].vy*(float)uiTime;
	m_Vertexes[uiParticle].Z+=m_Particles[uiParticle].vz*(float)uiTime;
	m_Particles[uiParticle].uiLifeTime += uiTime;
};

void Terrain::Load(const char *szFileName)
{
	Color.X =1; Color.Y = 1; Color.Z = 1; 

	DDS_IMAGE_INFORMATION	imgInfo;
	DDS_LoadFile(szFileName, m_uiTexture, &imgInfo);

	unsigned int uiUnderWater=0;
	unsigned int uiGrassCount=0;
	for(unsigned int uiRow=0; uiRow<MAP_SIZE_Y; uiRow++)
		for(unsigned int uiColumn = 0; uiColumn<MAP_SIZE_X; uiColumn++)
		{
			if(Map[uiRow][uiColumn] & MAP_WATER)
				uiUnderWater++;
			if(Map[uiRow][uiColumn] & MAP_GRASS)
				uiGrassCount++;
		}
	m_Vertexes.resize((MAP_SIZE_X*MAP_SIZE_Y-uiUnderWater+uiGrassCount)*6);
	m_Normals.resize((MAP_SIZE_X*MAP_SIZE_Y-uiUnderWater+uiGrassCount)*6);
	m_TexCoords.resize((MAP_SIZE_X*MAP_SIZE_Y-uiUnderWater+uiGrassCount)*6);
	m_Waters.resize(uiUnderWater);



	GLfloat	curX = 0.0f, curY = 0.0f;
	static const GLfloat rightCornerX = MAP_SIZE_X;
	static const GLfloat rightCornerY = MAP_SIZE_Y;

	GLfloat dx = (rightCornerX - curX)/(float)MAP_SIZE_X;
	GLfloat dy = (rightCornerY - curY)/(float)MAP_SIZE_Y;

	unsigned int uiCurVertex = 0;
	unsigned int uiCurrentWater = 0;
	for(unsigned int uiTileRow = 0; uiTileRow < MAP_SIZE_Y; uiTileRow++)
	{
		for(unsigned int uiTileColumn = 0; uiTileColumn < MAP_SIZE_X; uiTileColumn++)
		{
			if(!(Map[uiTileRow][uiTileColumn] & MAP_WATER))
			{
				//Land

				m_Vertexes[uiCurVertex].X = curX;		m_Vertexes[uiCurVertex].Y=curY;			m_Vertexes[uiCurVertex].Z = 0;
				m_Vertexes[uiCurVertex+1].X = curX+dx;	m_Vertexes[uiCurVertex+1].Y=curY+dy;	m_Vertexes[uiCurVertex+2].Z = 0;
				m_Vertexes[uiCurVertex+2].X = curX;		m_Vertexes[uiCurVertex+2].Y=curY+dy;	m_Vertexes[uiCurVertex+1].Z = 0;
	
				m_Vertexes[uiCurVertex+3].X = curX;		m_Vertexes[uiCurVertex+3].Y=curY;		m_Vertexes[uiCurVertex+3].Z = 0;
				m_Vertexes[uiCurVertex+4].X = curX+dx;	m_Vertexes[uiCurVertex+4].Y=curY;		m_Vertexes[uiCurVertex+4].Z = 0;
				m_Vertexes[uiCurVertex+5].X = curX+dx;	m_Vertexes[uiCurVertex+5].Y=curY+dy;	m_Vertexes[uiCurVertex+5].Z = 0;
				
	
				m_Normals[uiCurVertex].X	=m_Normals[uiCurVertex].Y=0;	m_Normals[uiCurVertex].Z=1;
				m_Normals[uiCurVertex+1].X	=m_Normals[uiCurVertex+1].Y=0;	m_Normals[uiCurVertex+1].Z=1;
				m_Normals[uiCurVertex+2].X	=m_Normals[uiCurVertex+2].Y=0;	m_Normals[uiCurVertex+2].Z=1;
				m_Normals[uiCurVertex+3].X	=m_Normals[uiCurVertex+3].Y=0;	m_Normals[uiCurVertex+3].Z=1;
				m_Normals[uiCurVertex+4].X	=m_Normals[uiCurVertex+4].Y=0;	m_Normals[uiCurVertex+4].Z=1;
				m_Normals[uiCurVertex+5].X	=m_Normals[uiCurVertex+5].Y=0;	m_Normals[uiCurVertex+5].Z=1;

				if(Map[uiTileRow][uiTileColumn] & MAP_STAFF)
				{	//graass
					m_TexCoords[uiCurVertex  ].X  = 0.0f; m_TexCoords[uiCurVertex  ].Y  = 0.0f;
					m_TexCoords[uiCurVertex+1].X  = 0.5f; m_TexCoords[uiCurVertex+1].Y  = 0.5f;
					m_TexCoords[uiCurVertex+2].X  = 0.0f; m_TexCoords[uiCurVertex+2].Y  = 0.5f;
					

					m_TexCoords[uiCurVertex+3].X  = 0.0f; m_TexCoords[uiCurVertex+3].Y  = 0.0f;
					m_TexCoords[uiCurVertex+4].X  = 0.5f; m_TexCoords[uiCurVertex+4].Y  = 0.0f;
					m_TexCoords[uiCurVertex+5].X  = 0.5f; m_TexCoords[uiCurVertex+5].Y  = 0.5f;
					
				}else
				{
					m_TexCoords[uiCurVertex  ].X  = 0.5f; m_TexCoords[uiCurVertex  ].Y  = 0.5f;
					m_TexCoords[uiCurVertex+1].X  = 1.0f; m_TexCoords[uiCurVertex+1].Y  = 1.0f;
					m_TexCoords[uiCurVertex+2].X  = 0.5f; m_TexCoords[uiCurVertex+2].Y  = 1.0f;
					

					m_TexCoords[uiCurVertex+3].X  = 0.5f; m_TexCoords[uiCurVertex+3].Y  = 0.5f;
					m_TexCoords[uiCurVertex+4].X  = 1.0f; m_TexCoords[uiCurVertex+4].Y  = 0.5f;
					m_TexCoords[uiCurVertex+5].X  = 1.0f; m_TexCoords[uiCurVertex+5].Y  = 1.0f;
					
				}
				uiCurVertex+=6;

				//Grass
				if(Map[uiTileRow][uiTileColumn] & MAP_GRASS)
				{
					m_Vertexes[uiCurVertex].X = curX;		m_Vertexes[uiCurVertex].Y=curY;			m_Vertexes[uiCurVertex].Z = 1.5f;
					m_Vertexes[uiCurVertex+1].X = curX+dx;	m_Vertexes[uiCurVertex+1].Y=curY+dy;	m_Vertexes[uiCurVertex+1].Z = 1.5f;
					m_Vertexes[uiCurVertex+2].X = curX;		m_Vertexes[uiCurVertex+2].Y=curY+dy;	m_Vertexes[uiCurVertex+2].Z = 1.5f;
		
					m_Vertexes[uiCurVertex+3].X = curX;		m_Vertexes[uiCurVertex+3].Y=curY;		m_Vertexes[uiCurVertex+3].Z = 1.5f;
					m_Vertexes[uiCurVertex+4].X = curX+dx;	m_Vertexes[uiCurVertex+4].Y=curY;		m_Vertexes[uiCurVertex+4].Z = 1.5f;
					m_Vertexes[uiCurVertex+5].X = curX+dx;		m_Vertexes[uiCurVertex+5].Y=curY+dy;		m_Vertexes[uiCurVertex+5 ].Z = 1.5f;
		
					m_Normals[uiCurVertex].X	=m_Normals[uiCurVertex].Y=0;	m_Normals[uiCurVertex].Z=1;
					m_Normals[uiCurVertex+1].X	=m_Normals[uiCurVertex+1].Y=0;	m_Normals[uiCurVertex+1].Z=1;
					m_Normals[uiCurVertex+2].X	=m_Normals[uiCurVertex+2].Y=0;	m_Normals[uiCurVertex+2].Z=1;
					m_Normals[uiCurVertex+3].X	=m_Normals[uiCurVertex+3].Y=0;	m_Normals[uiCurVertex+3].Z=1;
					m_Normals[uiCurVertex+4].X	=m_Normals[uiCurVertex+4].Y=0;	m_Normals[uiCurVertex+4].Z=1;
					m_Normals[uiCurVertex+5].X	=m_Normals[uiCurVertex+5].Y=0;	m_Normals[uiCurVertex+5].Z=1;

					m_TexCoords[uiCurVertex  ].X  = 0.0f; m_TexCoords[uiCurVertex  ].Y  = 0.5f;
					m_TexCoords[uiCurVertex+1].X  = 0.0f; m_TexCoords[uiCurVertex+1].Y  = 1.0f;
					m_TexCoords[uiCurVertex+2].X  = 0.5f; m_TexCoords[uiCurVertex+2].Y  = 1.0f;

					m_TexCoords[uiCurVertex+3].X  = 0.0f; m_TexCoords[uiCurVertex+3].Y  = 0.5f;
					m_TexCoords[uiCurVertex+4].X  = 0.5f; m_TexCoords[uiCurVertex+4].Y  = 1.0f;
					m_TexCoords[uiCurVertex+5].X  = 0.5f; m_TexCoords[uiCurVertex+5].Y  = 0.5f;
					uiCurVertex+=6;
				}
				
			}else
			{
				//Water
				m_Waters[uiCurrentWater].Pos.X = curX+dx/2.0f;
				m_Waters[uiCurrentWater].Pos.Y = curY+dy/2.0f;
				m_Waters[uiCurrentWater].Pos.Z = 0.5f;
				uiCurrentWater++;
			}
			curY +=dy;
		}
		curY =  0;
		curX += dx;
	}

	m_Flags |= IsLoaded|IsUseTexture;


}

void Terrain::Render()
{
	SPEED_TEST;
	RenderObject::Render();
	for(unsigned int ui=0; ui<m_Waters.size(); ui++)
	{
		m_Waters[ui].Render();
	}
}

void Terrain::Update (unsigned int time)
{
	SPEED_TEST;
	for(unsigned int ui=0; ui<m_Waters.size(); ui++)
	{
		m_Waters[ui].Update(time);
	}
}

Water::Water():SpecialFX()
{
	m_PointSize=500.0f; 
	m_nParticles = 10; 
	m_fPaused = false; 	
	m_uiLifeTime = 10; 
	m_Intensity = 0.001f;
	Load("./media/graphics/particle.dds");
}

Particle	Water::m_fnGenParticle()
{
	Particle result;
	static float fRand  = 0;
	fRand = rand()%100-50.0f;
	result.a = 0.5f + (float)fRand/30000.0f;
	result.r = 0.3f + (float)fRand/3000.0f;
	result.g = 0.5f + (float)fRand/700.0f;
	result.b = 0.8f + (float)fRand/1500.0f;

	result.x = 0.0f + (rand()%100-50.0f)/100.0f;
	result.y = 0.0f + (rand()%100-50.0f)/100.0f;
	result.z = 0.0f;
	result.uiLifeTime = 0;
	result.vx = (float)(rand()%100-50.0f)/300000.0f;
	result.vy = (float)-1.0f*(rand()%100-50.0f)/150000.0f;
	result.vz = 0;
	return result;
}

void		Water::m_fnUpdateParticle(unsigned int uiTime, unsigned int uiParticle)
{
	static float dColor = (float)uiTime/(float)m_uiLifeTime;
	SpecialFX::m_fnUpdateParticle(uiTime, uiParticle);

	if(m_Vertexes[uiParticle].Z<3)
	{
		Particle part = m_fnGenParticle();
		m_Vertexes[uiParticle].X = part.x;
		m_Vertexes[uiParticle].Y = part.y;
		m_Vertexes[uiParticle].Z = part.z;

		m_Colors[uiParticle].R = part.r;
		m_Colors[uiParticle].G = part.g;
		m_Colors[uiParticle].B = part.b;
		m_Colors[uiParticle].A = part.a;

		m_Particles[uiParticle].vx = part.vx;
		m_Particles[uiParticle].vy = part.vy;
		m_Particles[uiParticle].vz = part.vz;
	}
}